#pragma once

#include <fstream>
#include "detours.h"

#define NewLog(file) \
{ \
	std::wstring LogPath = file; \
	std::wfstream logFs; \
	logFs.open(LogPath.c_str(),std::ios::out); \
	logFs.close(); \
}


#define PrintLog(file, xLog) \
{ \
	std::wstring LogPath = file; \
	std::wfstream logFs; \
	logFs.open(LogPath.c_str(),std::ios::out | std::ios::app); \
	logFs << xLog; \
	logFs.close(); \
}

#define HIJACKCLASSFUNCTION(file, dllname,baseclass,funcname,functype,funcparam,classname) \
	{ \
		functype (* pfTarget)##funcparam##= \
			((##functype##(*)##funcparam##)(##baseclass##)->lpVtbl->##funcname); \
			functype (##classname##::* pfDetour)##funcparam##=&classname##::funcname##Replace; \
			functype (##classname##::* pfTrampoline)##funcparam##=&classname##::Real_##funcname; \
	    bool bDone = DetourFunctionWithEmptyTrampoline(*(PBYTE*)&pfTrampoline, \
										*(PBYTE*)&pfTarget, \
										*(PBYTE*)&pfDetour); \
		PrintLog(file, dllname << L" (register): " << #funcname << L" "); \
		if (bDone) \
		{ \
			PrintLog(file, L"Success\n"); \
		} \
		else \
		{ \
			PrintLog(file, L"Failed\n"); \
		} \
	}


#define HIJACKVTABLEFUNCTION(file, dllname,typedesc,funcname,classname,wrapperclassname) \
	{ \
		if (Real_##funcname == NULL) \
		{ \
			Real_##funcname = (##typedesc##)DetourFunction((unsigned char*)(##classname##)->lpVtbl->##funcname, \
			(unsigned char*)&wrapperclassname::funcname##Replace); \
			PrintLog(file, dllname << L" (register): ~::" << #funcname << L" "); \
			if (Real_##funcname) \
			{ \
				PrintLog(file,"Success\n"); \
			} \
			else \
			{ \
				PrintLog(file ,"Failed\n"); \
			} \
		} \
	}


#define HIJACKTRAMPOLINE(funcname,functype,funcparam,classname) DETOUR_TRAMPOLINE_EMPTY(functype classname##::Real_##funcname##funcparam##);

template<typename A, typename B> void HijackFunction(A original, B replacement, B& real)
{
	if(real == 0)
	{
		real = (B)DetourFunction((PBYTE)original, (PBYTE)replacement);
	}
}

#define DeclareHijack(ReturnValue, Function, Parameter, ParameterValues) \
	typedef ReturnValue (STDMETHODCALLTYPE *type##Function)Parameter; \
	static type##Function Function##Real; \
	static ReturnValue STDMETHODCALLTYPE Function##ReplacementWrapper Parameter \
{ \
	return SelfType::instance->Function##Replacement##ParameterValues; \
} \
	ReturnValue Function##Replacement Parameter

#define DefineHijack(Class, Function) Class::type##Function Class::Function##Real = 0;

#define PerformHijack(instance, Function) HijackFunction(instance->lpVtbl->Function, SelfType::Function##ReplacementWrapper, SelfType::Function##Real);

#define ReleaseHijack(Function) if(Function##Real != 0) DetourRemove((PBYTE)SelfType::Function##Real, (PBYTE)SelfType::Function##ReplacementWrapper)
